﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using WeixinPayResdk.Helper;
using WeixinPayResdk.Models;

namespace WeixinPayResdk
{
    public class WeixinPayHelper : DelegatingHandler
    {
        private string merchantId;
        private string serialNo;
        private string privateKey;
        private string appId;

        public WeixinPayHelper()
        {
            InnerHandler = new HttpClientHandler();
            this.appId = WeixinPayConfig.Appid;
            this.merchantId = WeixinPayConfig.merchantId;
            this.serialNo = WeixinPayConfig.serialNo;
            this.privateKey = ReadFile.APIClientKey(WeixinPayConfig.privateKeyFile);
        }

        /// <summary>
        /// 查询订单
        /// </summary>
        /// <param name="orderNumber">商户自己的订单号,不是微信生成的订单号</param>
        public async Task<WeixinPayStatusResModel> QueryOrder(string orderNumber)
        {
            var url = $"https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{orderNumber}?mchid={merchantId}";
            var client = new HttpClient(new WeixinPayHelper());
            var resp = await client.GetAsync(url);
            var respStr = await resp.Content.ReadAsStringAsync();
            var payModel = respStr.ToObject<WeixinPayStatusResModel>();
            return payModel;
        }

        /// <summary>
        /// 签名 预支付后统一下单需要
        /// </summary>
        /// <param name="message">格式必须：{appid}\n{timestamp}\n{randomValue}\nprepay_id={prepay_id}\n</param>
        /// <returns></returns>
        public static string Sign(string message)
        {
            // NOTE： 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
            //        亦不包括结尾的-----END PRIVATE KEY-----
            string privateKey = ReadFile.APIClientKey(WeixinPayConfig.privateKeyFile);

            //可移植方法
            //byte[] keyData = Convert.FromBase64String(privateKey);
            //var rsa = System.Security.Cryptography.RSA.Create();
            //rsa.ImportPkcs8PrivateKey(keyData, out _);
            //byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
            //return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));


            //该方法只能在windows上使用
            byte[] keyData = Convert.FromBase64String(privateKey);
            using (var cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob))
            using (var rsa = new RSACng(cngKey))
            {
                //HttpContext.Current.Response.Write(2);
                //HttpContext.Current.Response.End();
                byte[] data = Encoding.UTF8.GetBytes(message);
                return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
            }
        }

        /// <summary>
        /// 关闭订单处理
        /// </summary>
        /// <param name="orderNumber"></param>
        /// <returns></returns>
        public async Task<ClostOrderRespModel> ClostOrder(string orderNumber)
        {
            var url = $"https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{orderNumber}/close";
            var client = new HttpClient(new WeixinPayHelper());
            var bodyJson = new StringContent(new { mchid = this.merchantId }.ToJson(), Encoding.UTF8, "application/json");
            var resp = await client.PostAsync(url, bodyJson);
            var respStr = await resp.Content.ReadAsStringAsync();
            var closeModel = new ClostOrderRespModel();
            if (!string.IsNullOrEmpty(respStr))
                closeModel = respStr.ToObject<ClostOrderRespModel>();
            closeModel.StatusCode = resp.StatusCode.ToString();
            return closeModel;
        }


        /// <summary>
        /// 微信支付申请退款接口
        /// </summary>
        /// <param name="out_trade_no">商户系统内部订单号，只能是数字、大小写字母_-*且在同一个商户号下唯一;原支付交易对应的商户订单号。</param>
        /// <param name="out_refund_no">商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔。</param>
        /// <param name="reason">若商户传入，会在下发给用户的退款消息中体现退款原因。</param>
        /// <param name="refund">退款金额，币种的最小单位，只能为整数，不能超过原订单支付金额。</param>
        /// <param name="total">原支付交易的订单总金额，币种的最小单位，只能为整数。</param>
        /// <param name="currency">符合ISO 4217标准的三位字母代码，目前只支持人民币：CNY。</param>
        /// <returns></returns>
        public async Task<dynamic> Refunds(string out_trade_no, string out_refund_no, string reason, int refund, int total,  string currency = "CNY")
        {
            var req = new 
            {
                out_refund_no = out_refund_no,
                out_trade_no = out_trade_no,
                reason = reason,
                notify_url = WeixinPayConfig.refundsNotifyUrl,
                amount = new 
                {
                    refund = refund,
                    total = total,
                    currency = currency
                }
            };

            var url = $"https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
            var client = new HttpClient(new WeixinPayHelper());
            //Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1900009191",nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242",signature="uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4xZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8XyUgx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw==",timestamp="1554208460",serial_no="1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C"
            string uri = "/v3/refund/domestic/refunds";
            string body = JsonConvert.SerializeObject(req);
            var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
            string nonce = Path.GetRandomFileName();

            string signature = $"POST\n{uri}\n{timestamp}\n{nonce}\n{body}\n"; ;
            signature = WeixinPayHelper.Sign(signature);
            client.DefaultRequestHeaders.Add("Authorization", $"WECHATPAY2-SHA256-RSA2048 mchid=\"{WeixinPayConfig.merchantId}\",nonce_str=\"{nonce}\",signature=\"{signature}\",timestamp=\"{timestamp}\",serial_no=\"{WeixinPayConfig.serialNo}\"");
            client.DefaultRequestHeaders.Add("Accept", "application/json");
            client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36");
            var bodyJson = new StringContent(req.ToJson(), Encoding.UTF8, "application/json");

            var resp = await client.PostAsync(url, bodyJson);
            var respStr = await resp.Content.ReadAsStringAsync();
            var response = respStr.ToObject<WeixinPayCallbackRespModel>();
            var payModel = respStr.ToObject<RefundsRespModel>();
            if (string.IsNullOrEmpty(response.code) && string.IsNullOrEmpty(response.message))
            {
                response.message = "请求成功";
                response.code = "SUCCESS";
                response.data = payModel;
            }
            
            return response;
        }


        /// <summary>
        /// 查询退款结果
        /// </summary>
        /// <param name="out_refund_no"></param>
        /// <returns></returns>
        public async Task<QueryRefundsOrderRespModel> QueryRefundsOrder(string out_refund_no)
        {
            var url = $"https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/{out_refund_no}";
            var client = new HttpClient(new WeixinPayHelper());
            var resp = await client.GetAsync(url);
            var respStr = await resp.Content.ReadAsStringAsync();
            var payModel = respStr.ToObject<QueryRefundsOrderRespModel>();
            return payModel;
        }
    }
}
